#!/bin/python

import socket
import json
from os import getenv
from _thread import start_new_thread
from time import sleep


UDP_IP = getenv("SERVER_IP", "127.0.0.1")
UDP_PORT = int(getenv("SERVER_PORT", "1215"))
AFK_TIME = int(getenv("SERVER_AFK_TIME", "60"))
RECV_SIZE = int(getenv("SERVER_RECV_SIZE", "10"))
MAX_PLAYERS = int(getenv("SERVER_MAX_PLAYERS", "166"))

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

players = {}


def delete_afk_players() -> None:
    while 1:
        sleep(1)
        for addr in list(players.keys()):
            try:
                players[addr][("system", "time_before_kick")] -= 1
                if players[addr][("system", "time_before_kick")] <= 0:
                    player_data = players.pop(addr)
                    print(f"[AFK KICKED] {player_data['name']}")
            except Exception as e:
                print(f"[DELETE AFK ERROR {type(e)}] {e=} {addr=}")


def handle_data(data: dict, addr) -> None:
    if "delete" in data:
        players.pop(addr)
        return

    if "name" not in data:
        raise Exception("no 'name' in data")

    name = data["name"]
    if addr not in players:
        if len(players) >= MAX_PLAYERS:
            raise Exception(f"players limit reached. players limit: {MAX_PLAYERS}")
        names = tuple(map(lambda x: x["name"], players.values()))
        if name in names:
            raise Exception(f"player with {name=} already in game.")

    players[addr] = data
    players[addr][("system", "time_before_kick")] = AFK_TIME


def recv_and_send() -> None:
    while 1:
        rawdata, addr = sock.recvfrom(1024 * RECV_SIZE)
        try:
            data = json.loads(rawdata.decode("ascii"))
            print(f"[{addr}] {data}")
            handle_data(data, addr)
            send_data = json.dumps(list(players.values()), skipkeys=True)
            sock.sendto(send_data.encode("ascii"), addr)
            print(f"[RESPONSE] {send_data}")
        except Exception as e:
            print(f"[ERROR {type(e)}] {addr=} {e=} {rawdata=}")
            sock.sendto(f'{{"error": "{e}"}}'.encode("ascii"), addr)


if __name__ == "__main__":
    sock.bind((UDP_IP, UDP_PORT))
    start_new_thread(delete_afk_players, ())
    recv_and_send()
